
#include "Octogris.h"

//------------------------------------------------------------------------------------------
typedef Octogris<1, 2> Octogris_1x2_;
typedef Octogris<2, 2> Octogris_2x2_;

typedef Octogris<1, 4> Octogris_1x4_;
typedef Octogris<2, 4> Octogris_2x4_;
typedef Octogris<4, 4> Octogris_4x4_;

typedef Octogris<1, 6> Octogris_1x6_;
typedef Octogris<2, 6> Octogris_2x6_;
typedef Octogris<6, 6> Octogris_6x6_;

typedef Octogris<1, 8> Octogris_1x8_;
typedef Octogris<2, 8> Octogris_2x8_;
typedef Octogris<8, 8> Octogris_8x8_;

typedef Octogris<1, 12> Octogris_1x12_;
typedef Octogris<2, 12> Octogris_2x12_;
typedef Octogris<12, 12> Octogris_12x12_;

typedef Octogris<1, 32> Octogris_1x32_;
typedef Octogris<2, 32> Octogris_2x32_;
typedef Octogris<6, 32> Octogris_6x32_;
typedef Octogris<32, 32> Octogris_32x32_;

COMPONENT_ENTRY(Octogris_1x2_)
COMPONENT_ENTRY(Octogris_2x2_)

COMPONENT_ENTRY(Octogris_1x4_)
COMPONENT_ENTRY(Octogris_2x4_)
COMPONENT_ENTRY(Octogris_4x4_)

COMPONENT_ENTRY(Octogris_1x6_)
COMPONENT_ENTRY(Octogris_2x6_)
COMPONENT_ENTRY(Octogris_6x6_)

COMPONENT_ENTRY(Octogris_1x8_)
COMPONENT_ENTRY(Octogris_2x8_)
COMPONENT_ENTRY(Octogris_8x8_)

COMPONENT_ENTRY(Octogris_1x12_)
COMPONENT_ENTRY(Octogris_2x12_)
COMPONENT_ENTRY(Octogris_12x12_)

COMPONENT_ENTRY(Octogris_1x32_)
COMPONENT_ENTRY(Octogris_2x32_)
COMPONENT_ENTRY(Octogris_6x32_)
COMPONENT_ENTRY(Octogris_32x32_)

//------------------------------------------------------------------------------------------
template<int mNumberOfSources, int mNumberOfSpeakers>
Octogris<mNumberOfSources, mNumberOfSpeakers>::Octogris(AudioUnit component)
	: AUEffectBase(component, false)
{
	CreateElements();
	
//	int numberOfParameters = kConstantParameters + mNumberOfSources * kParamsPerSource + mNumberOfSpeakers * kParamsPerSpeakers;
	//Why did the original author not use the defined constant!? So aggravating. ---JT2021-04-23
	int numberOfParameters = mNumberOfParameters;
	Globals()->UseIndexedParameters(numberOfParameters);
	//printf("using %i parameters\n", numberOfParameters);

	//SetParameter(kLevelAttackParam, kLevelAttackDefault);
	//SetParameter(kLevelReleaseParam, kLevelReleaseDefault);
	SetParameter(kParamSmoothParam, kParamSmoothDefault);
	SetParameter(kLinkSourcesParam, 0);
	SetParameter(kShowGridLinesParam, 0);
	SetParameter(kMovementModeParam, 0);
	SetParameter(kShowLevelsParam, 0);
	SetParameter(kTakeAttenuationParam, 1);
//	SetParameter(kEnableJoystickParam, 0);
	SetParameter(kLinkMovementParam, 0);

	// - - - - - - - - - - - - - -
	// Sources default parameters
	for (int i = 0; i < mNumberOfSources; i++)
	{
		SetParameter(ParamForSourceD(i), kSourceDefaultDistance);
	}
	if (mNumberOfSources == 1)
	{
		SetParameter(ParamForSourceX(0), 0.5);
		SetParameter(ParamForSourceY(0), 0.5);
	}
	else
	{
		double anglePerSource = 2 * M_PI / mNumberOfSources;
		double offset, axisOffset;
/*		double anglePerSource = 2 * M_PI / mNumberOfSources;
		double offset = (anglePerSource + M_PI) / 2 - anglePerSource;
		for (int i = 0; i < mNumberOfSources; i++)
		{
			SetParameter(ParamForSourceX(i), cos(offset)*0.25+0.5);
			SetParameter(ParamForSourceY(i), 0.5 - sin(offset)*0.25);
			offset += anglePerSource;
		}
*/
		if(mNumberOfSources%2 == 0) //if the number of speakers is even we will assign them as stereo pairs
		{
			axisOffset = anglePerSource / 2;
			for (int i = 0; i < mNumberOfSources; i++)
			{
				
				if(i%2 == 0) //if this is the left channel subtract the offset
				{
					offset = M_PI / 2 - axisOffset;
				}
				else //right channel, add the offset and update axisOffset
				{
					offset = M_PI / 2 + axisOffset;
					axisOffset += anglePerSource;
				}
				
				SetParameter(ParamForSourceX(i), 0.5 - cos(offset)*0.25);
				SetParameter(ParamForSourceY(i), 0.5 - sin(offset)*0.25);
			}
		}
		else //odd number of speakers, assign in circular fashion
		{
			offset = (anglePerSource + M_PI) / 2 - anglePerSource;
			for (int i = 0; i < mNumberOfSources; i++)
			{
				SetParameter(ParamForSourceX(i), 0.5 - cos(offset)*0.5);
				SetParameter(ParamForSourceY(i), 0.5 - sin(offset)*0.5);
				offset += anglePerSource;
			}
		}
	}
	
	// - - - - - - - - - - - - - -
	// Speakers default parameters
	// Joseph Thibodeau Feb 2011---modified default speaker placement
	{
		double anglePerSpeakers = 2 * M_PI / mNumberOfSpeakers;
		double offset, axisOffset;
		
		if(mNumberOfSpeakers%2 == 0) //if the number of speakers is even we will assign them as stereo pairs
		{
			axisOffset = anglePerSpeakers / 2;
			for (int i = 0; i < mNumberOfSpeakers; i++)
			{
				SetParameter(ParamForSpeakerA(i), kSpeakerDefaultAttenuation);
				SetParameter(ParamForSpeakerM(i), 0);
				
				
				if(i%2 == 0) //if this is the left channel subtract the offset
				{
					offset = M_PI / 2 - axisOffset;
				}
				else //right channel, add the offset and update axisOffset
				{
					offset = M_PI / 2 + axisOffset;
					axisOffset += anglePerSpeakers;
				}
				
				SetParameter(ParamForSpeakerX(i), 0.5 - cos(offset)*0.5);
				SetParameter(ParamForSpeakerY(i), 0.5 - sin(offset)*0.5);
			}
		}
		else //odd number of speakers, assign in circular fashion
		{
			offset = (anglePerSpeakers + M_PI) / 2 - anglePerSpeakers;
			for (int i = 0; i < mNumberOfSpeakers; i++)
			{
				SetParameter(ParamForSpeakerA(i), kSpeakerDefaultAttenuation);
				SetParameter(ParamForSpeakerM(i), 0);
				
				SetParameter(ParamForSpeakerX(i), 0.5 - cos(offset)*0.5);
				SetParameter(ParamForSpeakerY(i), 0.5 - sin(offset)*0.5);
				offset += anglePerSpeakers;
			}
		}
	}
		
	mChannelInfo.inChannels = mNumberOfSources;
	mChannelInfo.outChannels = mNumberOfSpeakers;
	mCalculateLevels = false;
	
	for (int i = 0; i < mNumberOfSpeakers; i++) mLevels[i] = 0;
	mSmoothedParametersInited = false;
	mProcessCounter = 0;
	
#if AU_DEBUG_DISPATCHER
	mDebugDispatcher = new AUDebugDispatcher (this);
#endif
}

//------------------------------------------------------------------------------------------
template<int mNumberOfSources, int mNumberOfSpeakers>
Octogris<mNumberOfSources, mNumberOfSpeakers>::~Octogris()
{
#if AU_DEBUG_DISPATCHER
	delete mDebugDispatcher;
#endif
}

//------------------------------------------------------------------------------------------
template<int mNumberOfSources, int mNumberOfSpeakers>
void Octogris<mNumberOfSources, mNumberOfSpeakers>::PostConstructor()
{
	//LOG("PostConstructor\n")

	AUEffectBase::PostConstructor();

	if ( Inputs().GetNumberOfElements() > 0 )
	{
		const CAStreamBasicDescription curInStreamFormat = GetStreamFormat(kAudioUnitScope_Input, (AudioUnitElement)0);
	
		if ( ((UInt32)(mChannelInfo.inChannels) != curInStreamFormat.mChannelsPerFrame) && (mChannelInfo.inChannels >= 0) )
		{
			CAStreamBasicDescription newStreamFormat(curInStreamFormat);
			newStreamFormat.mChannelsPerFrame = (UInt32) (mChannelInfo.inChannels);
			AUBase::ChangeStreamFormat(kAudioUnitScope_Input, (AudioUnitElement)0, curInStreamFormat, newStreamFormat);
					
			//LOG("\tChanged input to %i\n", mChannelInfo.inChannels)
		}
	}
	
	const CAStreamBasicDescription curOutStreamFormat = GetStreamFormat(kAudioUnitScope_Output, (AudioUnitElement)0);

	if ( ((UInt32)(mChannelInfo.outChannels) != curOutStreamFormat.mChannelsPerFrame) && (mChannelInfo.outChannels >= 0) )
	{
		CAStreamBasicDescription newStreamFormat = CAStreamBasicDescription(curOutStreamFormat);
		newStreamFormat.mChannelsPerFrame = (UInt32) (mChannelInfo.outChannels);
		AUBase::ChangeStreamFormat(kAudioUnitScope_Output, (AudioUnitElement)0, curOutStreamFormat, newStreamFormat);
			
		//LOG("\tChanged output to %i\n", mChannelInfo.outChannels)
	}
}

//------------------------------------------------------------------------------------------
template<int mNumberOfSources, int mNumberOfSpeakers>
OSStatus Octogris<mNumberOfSources, mNumberOfSpeakers>::Reset(	AudioUnitScope 		inScope,
																AudioUnitElement 	inElement)
{
	printf("Octogris: Reset()\n");
	
	for (int i = 0; i < mNumberOfSpeakers; i++) mLevels[i] = 0;
	mSmoothedParametersInited = false;
	return AUEffectBase::Reset(inScope, inElement);
}

//------------------------------------------------------------------------------------------
template<int mNumberOfSources, int mNumberOfSpeakers>
OSStatus Octogris<mNumberOfSources, mNumberOfSpeakers>::GetParameterValueStrings(AudioUnitScope		inScope,
                                                                AudioUnitParameterID	inParameterID,
                                                                CFArrayRef *		outStrings)
{
	if (inScope != kAudioUnitScope_Global) return kAudioUnitErr_InvalidProperty;
	 

	if (inParameterID == kShowLevelsParam)
	{
		if (outStrings == NULL) return noErr;
		CFMutableArrayRef array = CFArrayCreateMutable(NULL, 2 + mNumberOfSources, &kCFTypeArrayCallBacks);
		
		CFArrayAppendValue(array, CFSTR("Do not show levels"));
		for (int i = 0; i < mNumberOfSources; i++)
		{
			CFStringRef s = CFStringCreateWithFormat(NULL, NULL, CFSTR("Source %i"), i+1);
			CFArrayAppendValue(array, s);
			CFRelease(s);
		}
		CFArrayAppendValue(array, CFSTR("All sources"));
	
		*outStrings = array;
        return noErr;
    }
	
	if (inParameterID == kMovementModeParam)
	{
		if (outStrings == NULL) return noErr;
		CFMutableArrayRef array = CFArrayCreateMutable(NULL, 2 + mNumberOfSources, &kCFTypeArrayCallBacks);
		
		CFArrayAppendValue(array, CFSTR("Independant"));
		if (mNumberOfSources == 2)
		{
			CFArrayAppendValue(array, CFSTR("SymetriX"));
			CFArrayAppendValue(array, CFSTR("SymetriY"));
			CFArrayAppendValue(array, CFSTR("SymetrXY"));
		}
		if (mNumberOfSources >= 2)
		{
			CFArrayAppendValue(array, CFSTR("Circular"));
			CFArrayAppendValue(array, CFSTR("CircFRad"));
			CFArrayAppendValue(array, CFSTR("CircFAng"));
			CFArrayAppendValue(array, CFSTR("CircFFix"));
			CFArrayAppendValue(array, CFSTR("DeltaLoc"));
		}
		
		*outStrings = array;
        return noErr;
    }

    return kAudioUnitErr_InvalidProperty;
}


//_____________________________________________________________________________
//
template<int mNumberOfSources, int mNumberOfSpeakers>
UInt32				Octogris<mNumberOfSources, mNumberOfSpeakers>::SupportedNumChannels (	const AUChannelInfo** 			outInfo)
{
	if (outInfo != NULL)
		*outInfo = &mChannelInfo;
	return 1;
}

//------------------------------------------------------------------------------------------
template<int mNumberOfSources, int mNumberOfSpeakers>
OSStatus			Octogris<mNumberOfSources, mNumberOfSpeakers>::GetParameterInfo(AudioUnitScope		inScope,
                                                        AudioUnitParameterID	inParameterID,
                                                        AudioUnitParameterInfo	&outParameterInfo )
{
	
    if (inScope != kAudioUnitScope_Global)
		return kAudioUnitErr_InvalidParameter;
		
	outParameterInfo.flags = 	kAudioUnitParameterFlag_IsWritable
						|		kAudioUnitParameterFlag_IsReadable;
	
	// More funny hacking business because of the incompatibility of new params in the "constant params"
	// category... special case for new param of Link Movement -- JT2012-04-23
	if ((int)inParameterID == kLinkMovementParam)
	{
		outParameterInfo.unit = kAudioUnitParameterUnit_Boolean;
		outParameterInfo.minValue = 0;
		outParameterInfo.maxValue = 1;
		outParameterInfo.defaultValue = 0;
		AUBase::FillInParameterName(outParameterInfo, CFSTR("Link Movement"), false);
		return noErr;
	}
						
	if ((int)inParameterID < kConstantParameters)
	{
		switch(inParameterID)
		{
			/*
			case kLevelAttackParam:
				outParameterInfo.unit = kAudioUnitParameterUnit_Milliseconds;
				outParameterInfo.minValue = kLevelAttackMin;
				outParameterInfo.maxValue = kLevelAttackMax;
				outParameterInfo.defaultValue = kLevelAttackDefault;
				AUBase::FillInParameterName(outParameterInfo, CFSTR("Level Attack"), false);
				break;
		
			case kLevelReleaseParam:
				outParameterInfo.unit = kAudioUnitParameterUnit_Milliseconds;
				outParameterInfo.minValue = kLevelReleaseMin;
				outParameterInfo.maxValue = kLevelReleaseMax;
				outParameterInfo.defaultValue = kLevelReleaseDefault;
				AUBase::FillInParameterName(outParameterInfo, CFSTR("Level Release"), false);
				break;
			*/
				
			case kParamSmoothParam:
				outParameterInfo.unit = kAudioUnitParameterUnit_Milliseconds;
				outParameterInfo.minValue = kParamSmoothMin;
				outParameterInfo.maxValue = kParamSmoothMax;
				outParameterInfo.defaultValue = kParamSmoothDefault;
				AUBase::FillInParameterName(outParameterInfo, CFSTR("Param. Smoothing"), false);
				break;
				
			case kLinkSourcesParam:
				outParameterInfo.unit = kAudioUnitParameterUnit_Boolean;
				outParameterInfo.minValue = 0;
				outParameterInfo.maxValue = 1;
				outParameterInfo.defaultValue = 0;
				AUBase::FillInParameterName(outParameterInfo, CFSTR("Link Sources"), false);
				break;
				
			case kShowGridLinesParam:
				outParameterInfo.unit = kAudioUnitParameterUnit_Boolean;
				outParameterInfo.minValue = 0;
				outParameterInfo.maxValue = 1;
				outParameterInfo.defaultValue = 0;
				AUBase::FillInParameterName(outParameterInfo, CFSTR("Show Grid"), false);
				break;
				
			case kMovementModeParam:
				outParameterInfo.unit = kAudioUnitParameterUnit_Indexed;
				outParameterInfo.minValue = 0;
				if (mNumberOfSources == 1) outParameterInfo.maxValue = 0;
				else if (mNumberOfSources > 2) outParameterInfo.maxValue = 5;
				else outParameterInfo.maxValue = 8;
				outParameterInfo.defaultValue = 0;
				AUBase::FillInParameterName(outParameterInfo, CFSTR("Movement Mode"), false);
				break;
				
			case kShowLevelsParam:
				outParameterInfo.unit = kAudioUnitParameterUnit_Indexed;
				outParameterInfo.minValue = 0;
				outParameterInfo.maxValue = mNumberOfSources + 1;
				outParameterInfo.defaultValue = 0;
				AUBase::FillInParameterName(outParameterInfo, CFSTR("Show Levels"), false);
				break;
				
			case kTakeAttenuationParam:
				outParameterInfo.unit = kAudioUnitParameterUnit_Boolean;
				outParameterInfo.minValue = 0;
				outParameterInfo.maxValue = 1;
				outParameterInfo.defaultValue = 1;
				AUBase::FillInParameterName(outParameterInfo, CFSTR("Take Attenuation"), false);
				break;
				
//			case kEnableJoystickParam:
//				outParameterInfo.unit = kAudioUnitParameterUnit_Boolean;
//				outParameterInfo.minValue = 0;
//				outParameterInfo.maxValue = 1;
//				outParameterInfo.defaultValue = 0;
//				AUBase::FillInParameterName(outParameterInfo, CFSTR("Enable Joystick"), false);
//				break;
		
			default:
				return kAudioUnitErr_InvalidParameter;
		}
		return noErr;
	}
	
	inParameterID -= kConstantParameters;
	if (inParameterID < mNumberOfSources * kParamsPerSource)
	{
		int t = inParameterID % kParamsPerSource;
		int s = inParameterID / kParamsPerSource + 1;
		switch(t)
		{
			case kSourceX:
				outParameterInfo.unit = kAudioUnitParameterUnit_Generic;
				outParameterInfo.minValue = 0;
				outParameterInfo.maxValue = 1;
				outParameterInfo.defaultValue = 0.5;
				AUBase::FillInParameterName(outParameterInfo, CFStringCreateWithFormat(NULL, NULL, CFSTR("Source %i - X"), s), true);
				break;
				
			case kSourceY:
				outParameterInfo.unit = kAudioUnitParameterUnit_Generic;
				outParameterInfo.minValue = 0;
				outParameterInfo.maxValue = 1;
				outParameterInfo.defaultValue = 0.5;
				AUBase::FillInParameterName(outParameterInfo, CFStringCreateWithFormat(NULL, NULL, CFSTR("Source %i - Y"), s), true);
				break;
				
			case kSourceD:
				outParameterInfo.unit = kAudioUnitParameterUnit_Generic;
				outParameterInfo.minValue = kSourceMinDistance;
				outParameterInfo.maxValue = kSourceMaxDistance;
				outParameterInfo.defaultValue = kSourceDefaultDistance;
				AUBase::FillInParameterName(outParameterInfo, CFStringCreateWithFormat(NULL, NULL, CFSTR("Source %i - Distance"), s), true);
				break;
		}
		return noErr;
	}
	
	inParameterID -= mNumberOfSources * kParamsPerSource;
	if (inParameterID < mNumberOfSpeakers * kParamsPerSpeakers)
	{
		int t = inParameterID % kParamsPerSpeakers;
		int s = inParameterID / kParamsPerSpeakers + 1;
		switch(t)
		{
			case kSpeakerX:
				outParameterInfo.unit = kAudioUnitParameterUnit_Generic;
				outParameterInfo.minValue = 0;
				outParameterInfo.maxValue = 1;
				outParameterInfo.defaultValue = 0.5;
				AUBase::FillInParameterName(outParameterInfo, CFStringCreateWithFormat(NULL, NULL, CFSTR("Speaker %i - X"), s), true);
				break;
				
			case kSpeakerY:
				outParameterInfo.unit = kAudioUnitParameterUnit_Generic;
				outParameterInfo.minValue = 0;
				outParameterInfo.maxValue = 1;
				outParameterInfo.defaultValue = 0.5;
				AUBase::FillInParameterName(outParameterInfo, CFStringCreateWithFormat(NULL, NULL, CFSTR("Speaker %i - Y"), s), true);
				break;
				
			case kSpeakerA:
				outParameterInfo.unit = kAudioUnitParameterUnit_Decibels;
				outParameterInfo.minValue = kSpeakerMinAttenuation;
				outParameterInfo.maxValue = kSpeakerMaxAttenuation;
				outParameterInfo.defaultValue = kSpeakerDefaultAttenuation;
				AUBase::FillInParameterName(outParameterInfo, CFStringCreateWithFormat(NULL, NULL, CFSTR("Speaker %i - Attenuation"), s), true);
				break;
				
			case kSpeakerM:
				outParameterInfo.unit = kAudioUnitParameterUnit_Boolean;
				outParameterInfo.minValue = 0;
				outParameterInfo.maxValue = 1;
				outParameterInfo.defaultValue = 0;
				AUBase::FillInParameterName(outParameterInfo, CFStringCreateWithFormat(NULL, NULL, CFSTR("Speaker %i - Muted"), s), true);
				break;
		}
		return noErr;
	}
	
	return kAudioUnitErr_InvalidParameter;
}

//------------------------------------------------------------------------------------------
template<int mNumberOfSources, int mNumberOfSpeakers>
OSStatus			Octogris<mNumberOfSources, mNumberOfSpeakers>::GetPropertyInfo (AudioUnitPropertyID	inID,
                                                        AudioUnitScope		inScope,
                                                        AudioUnitElement	inElement,
                                                        UInt32 &		outDataSize,
                                                        Boolean &		outWritable)
{
	if (inScope == kAudioUnitScope_Global)
	{
		switch (inID)
		{
			case kAudioUnitProperty_CocoaUI:
				outWritable = false;
				outDataSize = sizeof (AudioUnitCocoaViewInfo);
				return noErr;
				
			case kNumberOfSourcesPropertyID:
			case kNumberOfSpeakersPropertyID:
				outWritable = false;
				outDataSize = sizeof (int);
				return noErr;
				
			case kCalculateLevelsPropertyID:
				outWritable = true;
				outDataSize = sizeof (bool);
				return noErr;
				
			case kLevelsPropertyID:
				outWritable = false;
				outDataSize = sizeof (float) * mNumberOfSpeakers;
				return noErr;
				
			case kAudioUnitProperty_InPlaceProcessing:
				outWritable = false;
				outDataSize = sizeof (UInt32);
				return noErr;
				
			case kProcessCounterPropertyID:
				outWritable = false;
				outDataSize = sizeof (int64);
				return noErr;
		}
	}

	return AUEffectBase::GetPropertyInfo (inID, inScope, inElement, outDataSize, outWritable);
}

//------------------------------------------------------------------------------------------
template<int mNumberOfSources, int mNumberOfSpeakers>
OSStatus Octogris<mNumberOfSources, mNumberOfSpeakers>::GetProperty(	AudioUnitPropertyID inID,
                                                        AudioUnitScope 		inScope,
                                                        AudioUnitElement 	inElement,
                                                        void *			outData )
{
	if (inScope == kAudioUnitScope_Global)
	{
		switch (inID)
		{
			case kAudioUnitProperty_CocoaUI:
			{
				// Look for a resource in the main bundle by name and type.
				CFBundleRef bundle = CFBundleGetBundleWithIdentifier( CFSTR(kBundleIdentifier) );
				if (bundle == NULL) return fnfErr;

				CFURLRef bundleURL = CFBundleCopyBundleURL( bundle );
                if (bundleURL == NULL) return fnfErr;
                
				CFStringRef className = CFSTR("UDMOctogris_UIFactory");
				AudioUnitCocoaViewInfo cocoaInfo = { bundleURL, { className } };
				*((AudioUnitCocoaViewInfo *)outData) = cocoaInfo;
				
				return noErr;
			}
			
			case kNumberOfSourcesPropertyID:
				*((int *)outData) = mNumberOfSources;
				return noErr;
				
			case kNumberOfSpeakersPropertyID:
				*((int *)outData) = mNumberOfSpeakers;
				return noErr;

			case kCalculateLevelsPropertyID:
				*((bool *)outData) = mCalculateLevels;
				return noErr;
				
			case kLevelsPropertyID:
				memcpy(outData, mLevels, sizeof (float) * mNumberOfSpeakers);
				return noErr;
				
			case kProcessCounterPropertyID:
				*((int64 *)outData) = mProcessCounter;
				return noErr;
		}
	}
	

	return AUEffectBase::GetProperty (inID, inScope, inElement, outData);
}

//------------------------------------------------------------------------------------------
template<int mNumberOfSources, int mNumberOfSpeakers>
OSStatus Octogris<mNumberOfSources, mNumberOfSpeakers>::SetProperty(AudioUnitPropertyID 		inID,
																	AudioUnitScope 				inScope,
																	AudioUnitElement 			inElement,
																	const void *				inData,
																	UInt32 						inDataSize)
{
	if (inScope == kAudioUnitScope_Global)
	{
		switch (inID)
		{
			case kCalculateLevelsPropertyID:
				mCalculateLevels = *(bool *)inData;
				printf("mCalculateLevels: %i\n", mCalculateLevels);
				
				for (int i = 0; i < mNumberOfSpeakers; i++) mLevels[i] = 0;
				return noErr;
				
			case kAudioUnitProperty_InPlaceProcessing:
				return kAudioUnitErr_InvalidProperty;
		}
	}
	return AUEffectBase::SetProperty(inID, inScope, inElement, inData, inDataSize);
}

//------------------------------------------------------------------------------------------
template<int mNumberOfSources, int mNumberOfSpeakers>
void Octogris<mNumberOfSources, mNumberOfSpeakers>::ProcessData(float **inputs, float **outputs, float *params, float sampleRate, UInt32 frames)
{
	const float smooth = params[kParamSmoothParam]; // milliseconds
	const float sm_o = powf(0.01f, 1000.f / (smooth * sampleRate));
	const float sm_n = 1 - sm_o;
	
	// ramp all the parameters, except constant ones
	for (int i = kConstantParameters; i < mNumberOfParameters; i++)
	{
		float currentParam = mSmoothedParameters[i];
		float targetParam = params[i];
		float *ramp = mSmoothedParametersRamps[i];
	
		//float ori = currentParam;
		
		for (UInt32 f = 0; f < frames; f++)
		{
			currentParam = currentParam * sm_o + targetParam * sm_n;
			ramp[f] = currentParam;
		}
		
		//if (i == 0 && ori != currentParam) printf("param %i -> %f -> %f\n", i, ori, currentParam);
		
		mSmoothedParameters[i] = currentParam;
	}
	
	// compute
	const float adj_factor = 1 / sqrtf(2);
	for (int o = 0; o < mNumberOfSpeakers; o++)
	{
		float *output = outputs[o];
		float *output_x = mSmoothedParametersRamps[ParamForSpeakerX(o)];
		float *output_y = mSmoothedParametersRamps[ParamForSpeakerY(o)];
		float output_adj[frames];
		{
			float *output_m = mSmoothedParametersRamps[ParamForSpeakerM(o)];
			float *output_a = mSmoothedParametersRamps[ParamForSpeakerA(o)];
			
			for (UInt32 f = 0; f < frames; f++)
			{
				float a = dbToLinear(output_a[f]);
				float m = 1 - output_m[f];
				output_adj[f] = a * m;
			}
		}
		
		for (int i = 0; i < mNumberOfSources; i++)
		{
			float *input = inputs[i];
			float *input_x = mSmoothedParametersRamps[ParamForSourceX(i)];
			float *input_y = mSmoothedParametersRamps[ParamForSourceY(i)];
			float *input_d = mSmoothedParametersRamps[ParamForSourceD(i)];
			
			if (i == 0)
				for (UInt32 f = 0; f < frames; f++)
				{
					float dx = input_x[f] - output_x[f];
					float dy = input_y[f] - output_y[f];
					float d = sqrtf(dx*dx + dy*dy);
					float da = d * adj_factor * input_d[f];
					//if (da > 1) da = 0; else da = 1 - da;
					//da *= output_adj[f];
					//change distance scale for new calculations
					//"1" means far now instead of "0"
					if (da > 1) da = 1;
					//New distance vs amplitude calculations:
					//if da < 0.1 the log value will skyrocket:
					if (da < 0.1) da = 0.1;
					da = -log10(da); 
					da *= output_adj[f];
					output[f] = da * input[f];
				}
			else
				for (UInt32 f = 0; f < frames; f++)
				{
					float dx = input_x[f] - output_x[f];
					float dy = input_y[f] - output_y[f];
					float d = sqrtf(dx*dx + dy*dy);
					float da = d * adj_factor * input_d[f];
					//if (da > 1) da = 0; else da = 1 - da;
					//change distance scale for new calculations
					//"1" means far now instead of "0"
					if (da > 1) da = 1;
					//New distance vs amplitude calculations:
					//if da < 0.1 the log value will skyrocket:
					if (da < 0.1) da = 0.1;
					da = -log10(da);
					da *= output_adj[f];
					
					output[f] += da * input[f];
				}
		}
	}
}

//------------------------------------------------------------------------------------------
template<int mNumberOfSources, int mNumberOfSpeakers>
OSStatus Octogris<mNumberOfSources, mNumberOfSpeakers>::ProcessBufferLists(AudioUnitRenderActionFlags & ioActionFlags, 
												const AudioBufferList & oInBuffer,
												AudioBufferList & outBuffer, 
												UInt32 inFramesToProcess)
{
	// we do not support interleaved data
	if (((int)oInBuffer.mNumberBuffers != mNumberOfSources) || ((int)outBuffer.mNumberBuffers != mNumberOfSpeakers))
		return kAudioUnitErr_FormatNotSupported;

	ioActionFlags &= ~kAudioUnitRenderAction_OutputIsSilence;
	
	// cache values
	float params[mNumberOfParameters];
	for (int i = 0; i < mNumberOfParameters; i++)
		params[i] = GetParameter(i);

	float *inputs[mNumberOfSources];
	for (int i = 0; i < mNumberOfSources; i++)
		inputs[i] = (float*)(oInBuffer.mBuffers[i].mData);
		
	float *outputs[mNumberOfSpeakers];
	for (int o = 0; o < mNumberOfSpeakers; o++)
		outputs[o] = (float*)(outBuffer.mBuffers[o].mData);	
		
	float sampleRate = GetOutput(0)->GetStreamFormat().mSampleRate;
	UInt32 oriFramesToProcess = inFramesToProcess;
		
	// make sure the smoothed parameters table is inited
	if (!mSmoothedParametersInited)
	{
		memcpy(mSmoothedParameters, params, mNumberOfParameters * sizeof(float));
		mSmoothedParametersInited = true;
	}
	
	// check if ProcessesInPlace = false is ignored
	bool processesInPlaceIsIgnored = false;
	for (int i = 0; i < mNumberOfSources && !processesInPlaceIsIgnored; i++)
	{
		for (int o = 0; o < mNumberOfSpeakers; o++)
		{
			if (inputs[i] == outputs[o])
			{
				processesInPlaceIsIgnored = true;
				break;
			}
		}
	}
	
	// process data
	while(1)
	{
		UInt32 todo = (inFramesToProcess > 1024) ? 1024 : inFramesToProcess;
		
		if (processesInPlaceIsIgnored)
		{
			float *inputsCopy[mNumberOfSources];
			for (int i = 0; i < mNumberOfSources; i++)
			{
				memcpy(mInputsCopy[i], inputs[i], todo * sizeof(float));
				inputsCopy[i] = mInputsCopy[i];
			}
			
			ProcessData(inputsCopy, outputs, params, sampleRate, todo);
		}
		else
			ProcessData(inputs, outputs, params, sampleRate, todo);
		
		inFramesToProcess -= todo;
		if (inFramesToProcess == 0) break;
		
		for (int i = 0; i < mNumberOfSources; i++)
			inputs[i] += todo;
			
		for (int o = 0; o < mNumberOfSpeakers; o++)
			outputs[o] += todo; 
	}
	
	if (mCalculateLevels)
	{
		const float attack = kLevelAttackDefault; //params[kLevelAttackParam]; // milliseconds
		const float release = kLevelReleaseDefault; //params[kLevelReleaseParam]; // milliseconds
		const float ag = powf(0.01f, 1000.f / (attack * sampleRate));
		const float rg = powf(0.01f, 1000.f / (release * sampleRate));
		
		for (int o = 0; o < mNumberOfSpeakers; o++)
		{
			float *output = (float*)(outBuffer.mBuffers[o].mData);
			float env = mLevels[o];
			
			for (UInt32 f = 0; f < oriFramesToProcess; f++)
			{
				float s = fabsf(output[f]);
				float g = (s > env) ? ag : rg;
				env = g * env + (1.f - g) * s;
			}
			
			mLevels[o] = env;
		}
	}

	mProcessCounter++;
	return noErr;
}


